package level

import (
	"bytes"
	"encoding/gob"
	"reflect"

	"github.com/syndtr/goleveldb/leveldb/util"
	"google.golang.org/protobuf/proto"
)

// Query 查询返回对象，返回下一个游标.
func (p *Service) Query(prefix []byte, limit int, slice interface{}, start []byte) []byte {
	return p.QueryAndKey(prefix, limit, slice, start, nil)
}

func setValue(elem reflect.Value, data []byte) {
	if m, ok := elem.Interface().(proto.Message); ok {
		if err := proto.Unmarshal(data, m); err != nil {
			panic(err)
		}

		return
	}

	if m, ok := elem.Interface().(Loader); ok {
		if err := m.Load(data); err != nil {
			panic(err)
		}

		return
	}
	// []byte
	// if elem.Kind() != reflect.Ptr {
	// 	if elem.Elem().Kind() == reflect.Slice {
	// 		if elem.Elem().Type().Elem().Kind() == reflect.Uint8 {
	// 			elem.Elem().Set(reflect.ValueOf(bs))

	// 			return
	// 		}
	// 	}
	// }

	decoder := gob.NewDecoder(bytes.NewBuffer(data))
	if err := decoder.Decode(elem.Interface()); err != nil {
		panic(err)
	}
}

// QueryAndKey 查询返回对象，返回下一个游标.
func (p *Service) QueryAndKey(
	prefix []byte,
	limit int,
	slice interface{},
	start []byte,
	keys *[][]byte,
) []byte {
	sliceValue := reflect.ValueOf(slice)
	if sliceValue.Kind() != reflect.Ptr {
		panic(ErrNotPtr)
	}

	sliceValue = sliceValue.Elem()
	if sliceValue.Kind() != reflect.Slice {
		panic(ErrNotSlice)
	}

	ran := util.BytesPrefix(prefix)
	if len(start) > 0 {
		ran.Start = start
	}

	data := reflect.Append(sliceValue)
	elemType := reflect.TypeOf(slice).Elem().Elem()
	isPtr := false

	if elemType.Kind() == reflect.Ptr {
		elemType = elemType.Elem()
		isPtr = true
	}

	iter := p.DB.NewIterator(ran, nil)
	for i := 0; i < limit && iter.Next(); i++ {
		newElem := reflect.New(elemType)
		setValue(newElem, iter.Value())

		if isPtr {
			data = reflect.Append(data, newElem)
		} else {
			data = reflect.Append(data, newElem.Elem())
		}

		if keys != nil {
			key := iter.Key()
			ks := make([]byte, len(key))

			copy(ks, key)
			*keys = append(*keys, ks)
		}
	}

	sliceValue.Set(data)

	if iter.Next() {
		return iter.Key()
	}

	return []byte{}
}
